/*******************************************************************************
 * Copyright (c) 2011 EclipseDevX.org and others.
 * 
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at:
 * 
 *     http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/

package org.eclipsedevx.core.errors;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipsedevx.core.DevXCorePlugin;

class MarkersChangeService {

	private static MarkersChangeService SERVICE;

	public static MarkersChangeService getService() {
		if (SERVICE == null) {
			SERVICE = new MarkersChangeService();
		}

		return SERVICE;
	}

	public interface MarkerChangeListener {

		public void handleResourceChange();

	}

	private List<MarkerChangeListener> listeners = new ArrayList<MarkerChangeListener>();

	private IResourceChangeListener resourceChangeListener;

	private boolean inBuild;
	private boolean hadMarkerChanges;

	private MarkersChangeService() {

	}

	public synchronized void addListener(MarkerChangeListener listener) {
		if (!serviceRunning()) {
			startService();
		}

		if (!listeners.contains(listener)) {
			listeners.add(listener);
		}
	}

	public synchronized void removeListener(MarkerChangeListener listener) {
		listeners.remove(listener);

		if (listeners.isEmpty()) {
			stopService();
		}
	}

	private boolean serviceRunning() {
		return resourceChangeListener != null;
	}

	private void startService() {
		resourceChangeListener = new IResourceChangeListener() {
			public void resourceChanged(IResourceChangeEvent event) {
				handleChangeEvent(event);
			}
		};

		ResourcesPlugin.getWorkspace().addResourceChangeListener(
				resourceChangeListener,
				IResourceChangeEvent.POST_CHANGE
						| IResourceChangeEvent.PRE_BUILD
						| IResourceChangeEvent.POST_BUILD);
	}

	private void stopService() {
		if (resourceChangeListener != null) {
			ResourcesPlugin.getWorkspace().removeResourceChangeListener(
					resourceChangeListener);
		}

		resourceChangeListener = null;
	}

	private void handleChangeEvent(IResourceChangeEvent event) {
		if (event.getType() == IResourceChangeEvent.PRE_BUILD) {
			inBuild = true;
		} else if (event.getType() == IResourceChangeEvent.POST_BUILD) {
			inBuild = false;

			if (hadMarkerChanges) {
				notifyListeners();
			}
		} else {
			// POST_CHANGE
			int markerChangeCount = 0;

			for (String markerType : MarkersUtils.getInstance()
					.getErrorsViewMarkerIds()) {
				IMarkerDelta[] markerDeltas = event.findMarkerDeltas(
						markerType, true);

				// TODO: use the marker deltas to optimize our performance with
				// large numbers of markers

				// * @see IResourceDelta#ADDED
				// * @see IResourceDelta#REMOVED
				// * @see IResourceDelta#CHANGED

				markerChangeCount += markerDeltas.length;

				// All we're interested in is that we had some changes.
				if (markerChangeCount > 0) {
					break;
				}
			}

			if (markerChangeCount > 0) {
				if (inBuild) {
					hadMarkerChanges = true;
				} else {
					notifyListeners();
				}
			}
		}
	}

	private void notifyListeners() {
		List<MarkerChangeListener> listenersCopy = new ArrayList<MarkerChangeListener>(
				listeners);

		for (MarkerChangeListener listener : listenersCopy) {
			try {
				listener.handleResourceChange();
			} catch (Throwable t) {
				DevXCorePlugin.logError(t);
			}
		}
	}

}
